/* 
 * File:   Trabajo.cpp
 * Author: Maximiliano Monzon (pd 82341)
 * 
 * Created on April 26, 2013, 11:11 PM
 */

#include <sstream>
#include "client_Trabajo.h"
#include "common_Codec.h"

using namespace TP;
using namespace TP::Criptografia;

Trabajo::Trabajo() {
    _data = NULL;
}

Trabajo::~Trabajo() {
    if (NULL != _data)
    {
        delete []_data;
    }
}

void Trabajo::setData(string& pData){

    /* Se debe decodificar los datos a desencriptar.
     */
    Codec codec;
    _data = codec.decodificar (_data, _dataLongitud, pData);
    
}

void Trabajo::setNumeroProceso (string& pProceso){
    
    std::stringstream strm (pProceso);
    strm >> _numeroProceso;

}
    
void Trabajo::setLongitudClave(string& pLongitudClave){

    std::stringstream strm (pLongitudClave);
    strm >> _longitudClave;
}

void Trabajo::setRangoClaves (string& pRangoInicioClave, string& pRangoFinClave){

    std::stringstream strm (pRangoInicioClave);
    strm >> _rangoInicioClave;
    
    strm.clear();
    strm.str (pRangoFinClave);
    strm >> _rangoFinClave;
}

void Trabajo::iniciar() throw (Excepcion&){
    
    // Inicio el cursor de claves.
    _claveCursor = _rangoInicioClave;
    
}

bool Trabajo::quedanClaves () throw (Excepcion&){
    
    return (_rangoInicioClave <= _claveCursor && _claveCursor <= _rangoFinClave);
    
}

string* Trabajo::getClave() throw (Excepcion&){
    
    /* Itera el conjunto de claves hasta que encuentra una posible clave.
     * Si encuentra una posible clave detiene la iteracion y retorna el valor.
     * Retorna NULL cuando no encuentra una posible clave.
     */
    
    uint8_t* buffer = new uint8_t [_dataLongitud];
    uint8_t* key = new uint8_t [_longitudClave + 1];
    bool interpretable = false;
    
    while (quedanClaves() && !interpretable)
    {
        // Verifico si la clave actual es una posible clave.
        
        // Obtengo una representacion de array de bytes de la clave.
        bzero (key, _longitudClave + 1);    
        this->intToString(key, _longitudClave, _claveCursor);
        
        // Copio los datos a desencriptar en un buffer temporal.
        bzero (buffer, _dataLongitud);
        bcopy (_data, buffer, _dataLongitud);
        
        // Desencripto los datos con la clave.
        this->ed(buffer, _dataLongitud, key, _longitudClave);
        
        interpretable = this->dataInterpretable (buffer, _dataLongitud);
        
        // Conntinuo la busqueda con la siguiente clave.
        _claveCursor ++;
    }
    
    string *val = NULL;
    
    if (interpretable)
    {
        val = (string*) new string();
        *val = reinterpret_cast <char*> (key);
    }

    delete [] buffer;
    delete [] key;
    return val;
}

void Trabajo::intToString (uint8_t *pArray, uint32_t &pArraySize, const int &pNumber){

    // Obtengo la representacion literal del numero.
    
    std::stringstream strm;
    strm << pNumber;
    string strKey = "";
    strm >> strKey;

    if (pArraySize - strKey.length() >= 0)
    {
        // El array permite almacenar la representacion del numero.
        // Copio la representacion literal al array de bytes.
        
        bzero (pArray, pArraySize);
        bcopy(strKey.c_str(), &(pArray[ pArraySize - strKey.length() ]), strKey.length());
    }
}

int Trabajo::getNumeroProceso () {
    return _numeroProceso;
}

bool Trabajo::habilitado(){
    return (_tarea == "JOB-PART");
}

void Trabajo::ed(uint8_t *data, size_t ndata, const uint8_t *key, size_t nkey){
    uint8_t i = 0, j = 0, s[256];
    do { s[i] = i; } while (++i);
    do {
        j += s[i] + key[i % nkey];
        i - j && (s[i] ^= s[j], s[j] ^= s[i], s[i] ^= s[j]);
    } while (++i);
    j = 0;
    while (ndata--) {
        i++; j += s[i];
        i - j && (s[i] ^= s[j], s[j] ^= s[i], s[i] ^= s[j]);
        *data++ ^= s[(s[i] + s[j]) & 0xff];
    }
}

bool Trabajo::dataInterpretable (uint8_t *buffer, uint32_t pLongitud){

    bool interpretable = true;
    
    /* Si el buffer posee informacion interpretable, cada byte debe representar
     * un caracter ASCII (menor a 128).
     */
    
    for (uint32_t i = 0; i < pLongitud ; i++)
    {
        interpretable = interpretable && (buffer [i] <= 127);
    }
    
    return interpretable;
}

void Trabajo::setTarea(const string &pTarea){

    if (pTarea.length() > 0)
    {
        _tarea = pTarea;
    }
}